import UIKit

enum PhotoState {
    case new, downloaded, filtered, failed
}

class Photo {
    let name: String
    let url: String
    var image = UIImage(named: "Placeholder")
    var state: PhotoState = .new
    
    init(name: String, url: String) {
        self.name = name
        self.url = url
    }
}

class PhotoQueue {
    
    lazy var downloadsInProgress = [IndexPath: DownloadOperation]()
    lazy var downloadQueue : OperationQueue = {
        let queue = OperationQueue()
        queue.name = "download.queue"
        queue.maxConcurrentOperationCount = 1
        return queue
    }()
    
    lazy var filtersInProgress = [IndexPath: FilterOperation]()
    lazy var filterQueue : OperationQueue = {
        let queue = OperationQueue()
        queue.name = "filter.queue"
        queue.maxConcurrentOperationCount = 1
        return queue
    }()
    
    func suspendAllOperations() {
        self.downloadQueue.isSuspended = true
        self.filterQueue.isSuspended = true
    }
    
    func resumeAllOperations() {
        self.downloadQueue.isSuspended = false
        self.filterQueue.isSuspended = false
    }
}

class DownloadOperation: Operation {
    
    let photo: Photo
    
    init(_ photo: Photo) {
        self.photo = photo
    }
    
    override func main() {
        
        if isCancelled {
            return
        }
        
        guard let imageURL = URL(string:photo.url),
            let imageData = try? Data(contentsOf: imageURL) else {
                photo.state = .failed
                photo.image = UIImage(named: "Failed")
                return
        }
        
        if !imageData.isEmpty {
            photo.state = .downloaded
            photo.image = UIImage(data:imageData)
        } else {
            photo.state = .failed
            photo.image = UIImage(named: "Failed")
        }

    }
}

class FilterOperation: Operation {
    
    let photo: Photo
    
    init(_ photo: Photo) {
        self.photo = photo
    }

    override func main() {
        
        if isCancelled {
            return
        }
        
        guard photo.state == .downloaded else {
            return
        }
        
        if let image = photo.image,
            let filteredImage = applySepiaFilter(image) {
            
            photo.image = filteredImage
            photo.state = .filtered
        }
    }
        
    // MARK: - image processing
    func applySepiaFilter(_ image: UIImage) -> UIImage? {

      let inputImage = CIImage(data:UIImagePNGRepresentation(image)!)
      
        if isCancelled {
            return nil
        }
        
        let context = CIContext(options:nil)
      let filter = CIFilter(name:"CISepiaTone")
      filter?.setValue(inputImage, forKey: kCIInputImageKey)
      filter!.setValue(0.8, forKey: "inputIntensity")

      guard let outputImage = filter!.outputImage,
        let outImage = context.createCGImage(outputImage, from: outputImage.extent) else {
            photo.state = .failed
          return nil
      }
      return UIImage(cgImage: outImage)
    }
}

